package util

import (
	ckeadmapi "ckeadm/cmd/apis"
	ckeadmv1beta1 "ckeadm/cmd/apis/v1alpha1"
	"strings"
)

type CkeNodeSort []ckeadmv1beta1.CkeNode
type StatusInfoSort []ckeadmapi.InstallStatusInfo

func (cc CkeNodeSort) Len() int {
	return len(cc)
}

func (cc CkeNodeSort) Swap(i, j int) {
	cc[i], cc[j] = cc[j], cc[i]
}

func (cc CkeNodeSort) Less(i, j int) bool {
	return cc[i].Name < cc[j].Name
}

func (cc StatusInfoSort) Len() int {
	return len(cc)
}

func (cc StatusInfoSort) Swap(i, j int) {
	cc[i], cc[j] = cc[j], cc[i]
}

func (cc StatusInfoSort) Less(i, j int) bool {
	return cc[i].Name < cc[j].Name
}

func CheckAndSort(ckecomponents []ckeadmv1beta1.CkeComponent) (map[string][]string, [][]string) {
	firstCkecomponents := make([]string, 0)
	sortedCkecomponents := make([]string, 0)
	ckecomponentsSorting := make([][]string, 0)
	ckecomponentsDepend := make(map[string][]string)

	for _, ckecomponent := range ckecomponents {
		if ckecomponent.Labels["baseComponent"] == "true" && len(ckecomponent.Spec.Depend) == 0 {
			sortedCkecomponents = append(sortedCkecomponents, ckecomponent.Name)
			firstCkecomponents = append(firstCkecomponents, ckecomponent.Name)
			ckecomponentsDepend[ckecomponent.Name] = nil
			continue
		} else {
			ckecomponentsDepend[ckecomponent.Name] = ckecomponent.Spec.Depend
		}
	}
	ckecomponentsSorting = append(ckecomponentsSorting, firstCkecomponents)

	missingComponent := Check(ckecomponentsDepend)
	if len(missingComponent) != 0 {
		return missingComponent, nil
	}

	Sort(sortedCkecomponents, &ckecomponentsSorting, ckecomponentsDepend)

	return nil, ckecomponentsSorting
}

func Check(ckecomponentsDepend map[string][]string) map[string][]string {
	missingComponent := make(map[string][]string)
	for name, depend := range ckecomponentsDepend {
		for _, d := range depend {
			if strings.Contains(d, "|") {
				allMiss := true
				de := strings.Split(d, "|")
				for _, dep := range de {
					if _, ok := ckecomponentsDepend[dep]; ok {
						allMiss = false
						break
					}
				}
				if allMiss {
					missingComponent[name] = append(missingComponent[name], d)
				}
			} else if _, ok := ckecomponentsDepend[d]; !ok {
				missingComponent[name] = append(missingComponent[name], d)
			}
		}
	}
	return missingComponent
}

func Sort(sortedCkecomponents []string, ckecomponentsSorting *[][]string, ckecomponentsDepend map[string][]string) {
	for _, sortedCkecomponent := range sortedCkecomponents {
		delete(ckecomponentsDepend, sortedCkecomponent)
	}

	for {
		if len(ckecomponentsDepend) == 0 {
			break
		}
		activeCkecomponents := make([]string, 0)
		for name, depend := range ckecomponentsDepend {
			otherDepend := Difference(depend, sortedCkecomponents)
			if len(otherDepend) == 0 {
				activeCkecomponents = append(activeCkecomponents, name)
				delete(ckecomponentsDepend, name)
			} else {
				needSort := true
				for _, o := range otherDepend {
					if !strings.Contains(o, "|") {
						needSort = false
					}
				}
				if needSort {
					for _, o := range otherDepend {
						ot := strings.Split(o, "|")
						for _, ote := range ot {
							if ContainsStrings(sortedCkecomponents, ote) {
								activeCkecomponents = append(activeCkecomponents, name)
								delete(ckecomponentsDepend, name)
								break
							}
						}
					}
				}
			}
		}
		*ckecomponentsSorting = append(*ckecomponentsSorting, activeCkecomponents)
		sortedCkecomponents = append(sortedCkecomponents, activeCkecomponents...)
	}
}

func Difference(sliceA, sliceB []string) (result []string) {
	var keyInB = make(map[string]bool, 0)

	for item := range sliceB {
		keyInB[sliceB[item]] = true
	}

	for item := range sliceA {
		if !keyInB[sliceA[item]] {
			result = append(result, sliceA[item])
		}
	}

	return
}

func ContainsStrings(slice []string, s ...string) bool {
	return len(Difference(s, slice)) == 0
}

func RemoveStrings(slice []string, s ...string) (result []string) {
	return Difference(slice, s)
}

func AppendIfNotExist(slice []string, appends ...string) (result []string) {
	return append(slice, Difference(appends, slice)...)
}
